package config

import (
	"crypto/aes"
	"crypto/cipher"
	"encoding/base64"
	"fmt"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"os"
	"regexp"
)

func ExistConfig(configFileName string) bool {
	var findConfig bool
	_, err := os.Stat(configFileName)
	if err != nil {
		if os.IsNotExist(err) {
			findConfig = false
		} else {
			panic(err)
		}
	} else {
		findConfig = true
	}
	return findConfig
}

// 读取明文的配置文件
func ReadConfig[T any](fileName string, stru T) error {
	fmt.Printf("found config file: %s", fileName)

	bs, err := ReadConfig2Byte(fileName)
	if err != nil {
		fmt.Errorf("can't read " + fileName)
		return err
	}

	return UnmarshalConfig[T](bs, stru)
}

// 读取密文的配置文件
func ReadConfigForDecrypt[T any](fileName string, stru T, keyLen int) error {
	fmt.Printf("found config file: %s", fileName)

	bs, err := ReadConfig2Byte(fileName)
	if err != nil {
		fmt.Errorf("can't read " + fileName)
		return err
	}
	bs, err = ConfigDecrypt(bs, keyLen)
	if err != nil {
		return err
	}

	return UnmarshalConfig[T](bs, stru)
}

func ReadConfig2Byte(fileName string) ([]byte, error) {
	bs, err := ioutil.ReadFile(fileName)
	if err != nil {
		return []byte{}, err
	}
	return bs, nil
}

func UnmarshalConfig[T any](bs []byte, stru T) error {
	err := yaml.Unmarshal(bs, stru)
	if err != nil {
		fmt.Errorf("yaml.Unmarshal err:%v; row:%s", err, bs)
		return err
	}
	return nil
}

func findStrSubmatch(cipherText string) (string, string, error) {
	var Key, Value string
	re := regexp.MustCompile(`(.*)#(.*)`)

	matches := re.FindStringSubmatch(cipherText)
	if len(matches) > 0 {
		Key = matches[1]
		Value = matches[2]
	} else {
		return "", "", fmt.Errorf("no match found")
	}
	return Key, Value, nil
}

// Decrypt 解密
func ConfigDecrypt(cipherText []byte, keyLen int) ([]byte, error) {
	Key, Value, err := findStrSubmatch(string(cipherText))
	if err != nil {
		fmt.Println("Error:", err)
	}

	if len(Key) != keyLen {
		return cipherText, nil
	}

	cipherTextBytes, err := base64.URLEncoding.DecodeString(Value)
	if err != nil {
		return []byte{}, err
	}

	block, err := aes.NewCipher([]byte(Key))
	if err != nil {
		return []byte{}, err
	}

	if len(cipherTextBytes) < aes.BlockSize {
		return []byte{}, fmt.Errorf("ciphertext too short")
	}

	iv := cipherTextBytes[:aes.BlockSize]
	cipherTextBytes = cipherTextBytes[aes.BlockSize:]

	stream := cipher.NewCFBDecrypter(block, iv)
	stream.XORKeyStream(cipherTextBytes, cipherTextBytes)

	return cipherTextBytes, nil
}
